﻿/*
 * framedm.hpp
 *
 *  Created on: 2017年4月11日
 *      Author: work
 */

#ifndef _DM_PROTOCOL_FRAMEDM_HPP_
#define _DM_PROTOCOL_FRAMEDM_HPP_

#include <dm/protocol/frame.hpp>
#include <dm/bytes.hpp>

namespace dm{
namespace protocol{

template<int BufSize>
class TCFrameDm:public CFrame{
public:
	TCFrameDm();
	TCFrameDm( const TCFrameDm& frame );

	TCFrameDm& operator=( const TCFrameDm& frame );

	static inline int getBufMaxSize(){
		return BufSize;
	}

	inline const dm::uint8& getFun()const{
		return m_fun;
	}

	inline void setFun( const dm::uint8& fun ){
		m_fun = fun;
	}

	inline const dm::uint16& getLen()const{
		return m_len;
	}

	inline bool setLen( const dm::uint16& len ){
		if( len>BufSize )
			return false;
		m_len = len;
		return true;
	}

	inline const dm::uint8* getData()const{
		return m_data;
	}

	inline dm::uint8* getData(){
		return m_data;
	}

	bool setData( const dm::uint8* buf,const dm::uint16& len,const dm::uint16& pos=0 );

	bool encode( txbuf_t& buf )const;
	bool decode( const rxbuf_t& buf,const pos_t& end,pos_t& start,const CFrame* txFrame );

protected:
	template<typename T>
	inline bool set( const dm::uint8& fun,const T& asdu ){
		return set(fun,&asdu,sizeof(T));
	}

	template<typename T>
	inline bool get( const dm::uint8& fun,T& asdu )const{
		return get(fun,&asdu,sizeof(T));
	}

	template<typename T>
	inline bool set( const dm::uint8& fun,const dm::uint16& count,const T* asdu ){
		return set(fun,count,asdu,sizeof(T)*count);
	}

	template<typename T>
	bool get( const dm::uint8& fun,dm::uint16& count,T* asdu,const dm::uint16& asduSize )const{
		return get(fun,count,asdu,sizeof(T),asduSize);
	}

	template<typename T>
	inline bool set( const dm::uint8& fun,const dm::uint32& start,const dm::uint16& count,const T* asdu ){
		return set(fun,start,count,asdu,sizeof(T)*count);
	}

	template<typename T>
	inline bool get( const dm::uint8& fun,dm::uint32& start,dm::uint16& count,T* asdu,const dm::uint16& asduSize )const{
		return get(fun,start,count,asdu,sizeof(T),asduSize );
	}

	bool set( const dm::uint8& fun,const void* buf,const dm::uint16& len );
	bool get( const dm::uint8& fun,void* buf,const dm::uint16& len )const;

	bool set( const dm::uint8& fun,const dm::uint16& count,const void* buf,const dm::uint16& len );
	bool get( const dm::uint8& fun,dm::uint16& count,void* buf,const dm::uint16& blockSize,const dm::uint16& blockCount )const;

	bool set( const dm::uint8& fun,const dm::uint32& start,const dm::uint16& count,const void* buf,const dm::uint16& len );
	bool get( const dm::uint8& fun,dm::uint32& start,dm::uint16& count,void* buf,const dm::uint16& blockSize,const dm::uint16& blockCount )const;

	bool set( const dm::uint8& fun,const dm::uint32& start,const dm::uint16& count );
	bool get( const dm::uint8& fun,dm::uint32& start,dm::uint16& count )const;
protected:
	dm::uint8 m_fun;

	dm::uint16 m_len;
	dm::uint8 m_data[BufSize];
};

/**
 * 构造函数
 */
template<int BufSize>
TCFrameDm<BufSize>::TCFrameDm():CFrame(),m_fun(0),m_len(0){
}

/**
 * 拷贝构造函数
 * @param frame
 */
template<int BufSize>
TCFrameDm<BufSize>::TCFrameDm( const TCFrameDm<BufSize>& frame ):CFrame(frame),m_fun(frame.m_fun),m_len(frame.m_len){
	for( dm::uint16 i=0;i<m_len;++i )
		m_data[i] = frame.m_data[i];
}

/**
 * 赋值操作
 * @param frame
 * @return
 */
template<int BufSize>
TCFrameDm<BufSize>& TCFrameDm<BufSize>::operator =( const TCFrameDm<BufSize>& frame ){
	m_fun = frame.m_fun;
	m_len = frame.m_len;
	for( dm::uint16 i=0;i<m_len;++i )
		m_data[i] = frame.m_data[i];

	return *this;
}

/**
 * 设置数据
 * @param buf 缓冲区
 * @param len 长度
 * @param pos 偏移
 * @return
 */
template<int BufSize>
bool TCFrameDm<BufSize>::setData( const dm::uint8* buf,const dm::uint16& len,const dm::uint16& pos ){
	if( pos+len>m_len )
		return false;
	for( dm::uint16 i=0;i<len;++i )
		m_data[pos+i] = buf[i];
	return true;
}

/**
 * 编码
 * @param buf 发送缓冲区
 * @return
 */
template<int BufSize>
bool TCFrameDm<BufSize>::encode( txbuf_t& buf )const{
	dm::uint16 cs = 0;

	//! 起始符0x68
	buf.push(0x68);
	cs += 0x68;

	//! 长度
	buf.push(dm::lowByte_u16(m_len+1u));
	cs += dm::lowByte_u16(m_len+1u);

	buf.push(dm::higByte_u16(m_len+1u));
	cs += dm::higByte_u16(m_len+1u);

	//! 分隔符 0x68
	buf.push(0x68);
	cs += 0x68;

	//! 功能码
	buf.push(m_fun);
	cs += m_fun;

	//! 数据
	if( m_len>0 ){
		buf.push(m_data,m_len);
		for( dm::uint16 i=0;i<m_len;++i ){
			cs += m_data[i];
		}
	}

	//! 校验和
	buf.push( dm::lowByte_u16(cs));
	buf.push( dm::higByte_u16(cs));

	//! 结束符 0x16
	buf.push(0x16);

	return true;
}

/**
 * 解析
 * @param buf 接收缓冲区
 * @param end 接收缓冲区结尾位置
 * @param start 接收缓冲区开始位置
 * @param
 * @return 是否解析成功
 */
template<int BufSize>
bool TCFrameDm<BufSize>::decode( const rxbuf_t& buf,const pos_t& end,pos_t& start,const CFrame* ){
	while( true ){
		// 查找起始字符
		while( buf.dataAt(start)!=0x68 && start!=end )
			++start;

		dm::uint len = end - start;

		// 最小长度是S帧或者U帧6个字符，紧跟着是APDU长度域
		if( len<8u )
			return false;

		dm::uint16 cs = 0x68;

		dm::uint16 tmp = dm::bytes2uint16(buf.dataAt(start+1),buf.dataAt(start+2));
		cs += buf.dataAt(start+1);
		cs += buf.dataAt(start+2);

		if( len<7u+tmp )
			return false;

		if( buf.dataAt(start+3)!=0x68 ){
			++start;
			continue;
		}

		if( tmp-1>BufSize ){
			++start;
			continue;
		}

		if( buf.dataAt(start+tmp+6)!=0x16 ){
			++start;
			continue;
		}

		cs += 0x68;

		// 可以解析内容了。
		m_fun = buf.dataAt( start+4 );
		cs += m_fun;

		m_len = tmp-1;
		for( dm::uint16 i=0;i<m_len;++i ){
			m_data[i] = buf.dataAt(start+5+i);
			cs += m_data[i];
		}

		if( cs!=dm::bytes2uint16(buf.dataAt(start+5+m_len),buf.dataAt(start+6+m_len)) ){
			++start;
			continue;
		}

		start += 8+m_len;

		return true;
	}
}

/**
 * 设置功能码，及数据缓冲区
 * @param fun
 * @param buf
 * @param len
 * @return
 */
template<int BufSize>
bool TCFrameDm<BufSize>::set( const dm::uint8& fun,const void* buf,const dm::uint16& len ){
	if( len>BufSize )
		return false;
	m_fun = fun;
	const dm::uint8* p = (const dm::uint8*)buf;
	for( dm::uint16 i=0;i<len;++i )
		m_data[i] = p[i];

	return true;
}

/**
 * 测试功能码并获取数据缓冲区
 * @param fun 指定功能码
 * @param buf 数据缓冲区
 * @param len 指定长度
 * @return
 */
template<int BufSize>
bool TCFrameDm<BufSize>::get( const dm::uint8& fun,void* buf,const dm::uint16& len )const{
	if( m_fun!=fun || m_len!=len )
		return false;
	dm::uint8* p = (dm::uint8*)buf;
	for( dm::uint16 i=0;i<len;++i )
		p[i] = m_data[i];

	return true;
}

/**
 * 设置含有uint16类型的计数字段
 * @param fun
 * @param count
 * @param buf
 * @param len
 * @return
 */
template<int BufSize>
bool TCFrameDm<BufSize>::set( const dm::uint8& fun,const dm::uint16& count,const void* buf,const dm::uint16& len ){
	if( len+2>BufSize )
		return false;
	m_fun = fun;
	m_data[0] = dm::lowByte_u16(count);
	m_data[1] = dm::higByte_u16(count);

	const dm::uint8* p = (const dm::uint8*)buf;
	for( dm::uint16 i=0;i<len;++i )
		m_data[i+2] = p[i];

	return true;
}

template<int BufSize>
bool TCFrameDm<BufSize>::get( const dm::uint8& fun,dm::uint16& count,void* buf,const dm::uint16& blockSize,const dm::uint16& blockCount )const{
	if( m_fun!=fun || m_len>(blockSize*blockCount+2) )
		return false;

	count = dm::bytes2uint16( m_data[0],m_data[1] );
	if( count>blockCount )
		return false;

	dm::uint8* p = (dm::uint8*)buf;

	for( dm::uint16 i=0;i<m_len-2;++i )
		p[i] = m_data[i+2];

	return true;
}

template<int BufSize>
bool TCFrameDm<BufSize>::set( const dm::uint8& fun,const dm::uint32& start,const dm::uint16& count,const void* buf,const dm::uint16& len ){
	if( len+6>BufSize )
		return false;
	m_fun = fun;

	m_data[0] = dm::lowByte_u32(start,0);
	m_data[1] = dm::lowByte_u32(start,1);
	m_data[2] = dm::lowByte_u32(start,2);
	m_data[3] = dm::lowByte_u32(start,3);

	m_data[4] = dm::lowByte_u16(count);
	m_data[5] = dm::higByte_u16(count);

	const dm::uint8* p = (const dm::uint8*)buf;
	for( dm::uint16 i=0;i<len;++i )
		m_data[i+6] = p[i];

	m_len = 6 + len;

	return true;
}

template<int BufSize>
bool TCFrameDm<BufSize>::get( const dm::uint8& fun,dm::uint32& start,dm::uint16& count,void* buf,const dm::uint16& blockSize,const dm::uint16& blockCount )const{
	if( m_fun!=fun || m_len>(blockSize*blockCount+6) )
		return false;

	start = dm::bytes2uint32( m_data[0],m_data[1],m_data[2],m_data[3] );
	count = dm::bytes2uint16( m_data[4],m_data[5] );
	if( count>blockCount )
		return false;

	dm::uint8* p = (dm::uint8*)buf;
	for( dm::uint16 i=0;i<m_len-6;++i )
		p[i] = m_data[i+6];

	return true;
}

template<int BufSize>
bool TCFrameDm<BufSize>::set( const dm::uint8& fun,const dm::uint32& start,const dm::uint16& count ){
	m_fun = fun;

	m_data[0] = dm::lowByte_u32(start,0);
	m_data[1] = dm::lowByte_u32(start,1);
	m_data[2] = dm::lowByte_u32(start,2);
	m_data[3] = dm::lowByte_u32(start,3);

	m_data[4] = dm::lowByte_u16(count);
	m_data[5] = dm::higByte_u16(count);

	m_len = 6;

	return true;
}

template<int BufSize>
bool TCFrameDm<BufSize>::get( const dm::uint8& fun,dm::uint32& start,dm::uint16& count )const{
	if( m_fun!=fun || m_len!=6 )
		return false;

	start = dm::bytes2uint32( m_data[0],m_data[1],m_data[2],m_data[3] );
	count = dm::bytes2uint16( m_data[4],m_data[5] );

	return true;
}

}
}

#endif /* INCLUDE_DM_PROTOCOL_FRAMEDM_HPP_ */
